package com.dragon.service.impl.cpp;

import cn.hutool.core.io.FileUtil;
import com.dragon.pojo.ExecuteMessage;
import com.dragon.service.impl.CommonServiceImpl;
import com.dragon.utils.ProcessUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Slf4j
@Component("cpp")
public class CppServiceImpl extends CommonServiceImpl {
    @Override
    public File saveCodeToFile(String code) {
        final String GLOBAL_CODE_DIR_NAME = "codeTemp";
        // 获取当前运行程序的工作目录
        String userDir = System.getProperty("user.dir");
        // 构建全局代码存储目录的完整路径(userDir + 文件分隔符 + 全局目录名)
        String globalCodePathName = userDir + File.separator + GLOBAL_CODE_DIR_NAME;
        log.info("globalCodePathName:{}",globalCodePathName);
        // 如果目录不存在则创建
        if(!FileUtil.exist(globalCodePathName)){
            FileUtil.mkdir(globalCodePathName);
        }
        // 使用UUID生成目录名
        String userCodeParentPath = globalCodePathName + File.separator + UUID.randomUUID();
        // 构造具体的java文件路径
        String userCodePath = userCodeParentPath + File.separator + "main.cpp";

        return FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
    }

    @Override
    public ExecuteMessage compileFile(File userCodeFile) {
        // 获取可执行文件路径
        String executablePath = userCodeFile.getParent() + File.separator + "main";
        // 构造编译命令
        String compileCmd = String.format("g++ %s -o %s -O2 -std=c++17", userCodeFile.getAbsolutePath(), executablePath);
        try{
            // 进行编译
            Process compileProcess = Runtime.getRuntime().exec(compileCmd);
            // 运行并监听编译过程
            ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(compileProcess, "编译",null,10000L);
            // 判断编译是否成功
            if(executeMessage.getExitValue() != 0){
                // 如果编译失败，删除临时文件
                boolean del = FileUtil.del(userCodeFile.getParentFile());
                log.info("编译失败，删除:{}",del ? "成功" : "失败");
            }
            log.info("compileMessage:{}",executeMessage);
            return executeMessage;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<ExecuteMessage> runFile(File userCodeFile, List<String> inputList, long timeLimit) {
        // 获取 python 包装器文件路径
        String wrapperPath = System.getProperty("user.dir") + File.separator + "scripts" + File.separator + "python_memory_wrapper.py";

        List<ExecuteMessage> executeMessagesList = new ArrayList<>();
        // 判断当前操作系统
        boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");

        // 获取编译后可执行文件的文件路径
        String executablePath = userCodeFile.getParent() + File.separator + "main";
        // Windows 可执行文件为 .exe
        if (isWindows) {
            executablePath += ".exe";
        }
        for (String inputArgs : inputList) {
            List<String> commandList = new ArrayList<>();
            if(isWindows){
                // Windows 环境下的命令，不包含资源限制
                // 调用包装器
                commandList.add("python");
                commandList.add(wrapperPath);
                // 传递要被执行的命令
                commandList.add(executablePath);
            } else {
                String subCommand = String.format("python3 %s %s", wrapperPath, executablePath);
                String fullCommand = String.format("ulimit -v %d; %s", 262144, subCommand);
                commandList.add("/bin/bash");
                commandList.add("-c");
                commandList.add(fullCommand);
            }
            try {
                Process runProcess = Runtime.getRuntime().exec(commandList.toArray(new String[0]));
                log.info("程序输入:{}",inputArgs);
                ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(runProcess, "运行",inputArgs + "\n",timeLimit + 2000L);
                log.info("输出结果: {}", executeMessage.getMessage());
                log.info("executeMessage:{}",executeMessage);
                executeMessagesList.add(executeMessage);
            } catch (Exception e) {
                log.error("执行用户代码时出错", e);
                throw new RuntimeException(e);
            }
        }
        return executeMessagesList;
    }

}
